home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / pyshared / DistUpgrade / DistUpgradeFetcherCore.py < prev    next >
Encoding:
Python Source  |  2009-04-27  |  10.6 KB  |  277 lines

  1. # DistUpgradeFetcherCore.py 
  2. #  
  3. #  Copyright (c) 2006 Canonical
  4. #  
  5. #  Author: Michael Vogt <michael.vogt@ubuntu.com>
  6. #  This program is free software; you can redistribute it and/or 
  7. #  modify it under the terms of the GNU General Public License as 
  8. #  published by the Free Software Foundation; either version 2 of the
  9. #  License, or (at your option) any later version.
  10. #  This program is distributed in the hope that it will be useful,
  11. #  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. #  GNU General Public License for more details.
  14. #  You should have received a copy of the GNU General Public License
  15. #  along with this program; if not, write to the Free Software
  16. #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  17. #  USA
  18.  
  19. from string import Template
  20. import os
  21. import apt_pkg
  22. import apt
  23. import tarfile
  24. import socket
  25. import urlparse
  26. import urllib2
  27. import tempfile
  28. import shutil
  29. import sys
  30. import GnuPGInterface
  31. from gettext import gettext as _
  32. from aptsources.sourceslist import SourcesList
  33.  
  34. from utils import *
  35.  
  36. class DistUpgradeFetcherCore(object):
  37.     " base class (without GUI) for the upgrade fetcher "
  38.  
  39.     DEFAULT_MIRROR="http://archive.ubuntu.com/ubuntu"
  40.     DEFAULT_COMPONENT="main"
  41.     DEBUG = "DEBUG_UPDATE_MANAGER" in os.environ
  42.  
  43.     def __init__(self, new_dist, progress):
  44.         self.new_dist = new_dist
  45.         self.current_dist_name = get_dist()
  46.         self._progress = progress
  47.         # options to pass to the release upgrader when it is run
  48.         self.run_options = []
  49.  
  50.     def _debug(self, msg):
  51.         " helper to show debug information "
  52.         if self.DEBUG:
  53.             sys.stderr.write(msg+"\n")
  54.  
  55.     def showReleaseNotes(self):
  56.         return True
  57.  
  58.     def error(self, summary, message):
  59.         """ dummy implementation for error display, should be overwriten
  60.             by subclasses that want to more fancy method
  61.         """
  62.         print summary
  63.         print message
  64.         return False
  65.  
  66.     def authenticate(self):
  67.         if self.new_dist.upgradeToolSig:
  68.             f = self.tmpdir+"/"+os.path.basename(self.new_dist.upgradeTool)
  69.             sig = self.tmpdir+"/"+os.path.basename(self.new_dist.upgradeToolSig)
  70.             print "authenticate '%s' against '%s' " % (os.path.basename(f),os.path.basename(sig))
  71.             if not self.gpgauthenticate(f, sig):
  72.                 return False
  73.  
  74.         # we may return False here by default if we want to make a sig
  75.         # mandatory
  76.         return True
  77.  
  78.     def gpgauthenticate(self, file, signature,
  79.                         keyring='/etc/apt/trusted.gpg'):
  80.         """ authenticated a file against a given signature, if no keyring
  81.             is given use the apt default keyring
  82.         """
  83.         gpg = GnuPGInterface.GnuPG()
  84.         gpg.options.extra_args = ['--no-options',
  85.                                   '--homedir',self.tmpdir,
  86.                                   '--no-default-keyring',
  87.                                   '--ignore-time-conflict',
  88.                                   '--keyring', keyring]
  89.         proc = gpg.run(['--verify', signature, file],
  90.                        create_fhs=['status','logger','stderr'])
  91.         gpgres = proc.handles['status'].read()
  92.         try:
  93.             proc.wait()
  94.         except IOError,e:
  95.             # gnupg returned a problem (non-zero exit)
  96.             print "exception from gpg: %s" % e
  97.             print "Debug information: "
  98.             print proc.handles['status'].read()
  99.             print proc.handles['stderr'].read()
  100.             print proc.handles['logger'].read()
  101.             return False
  102.         if "VALIDSIG" in gpgres:
  103.             return True
  104.         print "invalid result from gpg:"
  105.         print gpgres
  106.         return False
  107.  
  108.     def extractDistUpgrader(self):
  109.           # extract the tarbal
  110.           fname = os.path.join(self.tmpdir,os.path.basename(self.uri))
  111.           print "extracting '%s'" % os.path.basename(fname)
  112.           if not os.path.exists(fname):
  113.               return False
  114.           try:
  115.               tar = tarfile.open(self.tmpdir+"/"+os.path.basename(self.uri),"r")
  116.               for tarinfo in tar:
  117.                   tar.extract(tarinfo)
  118.               tar.close()
  119.           except tarfile.ReadError, e:
  120.               logging.error("failed to open tarfile (%s)" % e)
  121.               return False
  122.           return True
  123.  
  124.     def verifyDistUprader(self):
  125.         # FIXME: check a internal dependency file to make sure
  126.         #        that the script will run correctly
  127.           
  128.         # see if we have a script file that we can run
  129.         self.script = script = "%s/%s" % (self.tmpdir, self.new_dist.name)
  130.         if not os.path.exists(script):
  131.             return self.error(_("Could not run the upgrade tool"),
  132.                          _("Could not run the upgrade tool") + ".  " + _("This is most likely a bug in the upgrade tool. "
  133.                           "Please report it as a bug"))
  134.         return True
  135.  
  136.     def mirror_from_sources_list(self, uri, default_uri):
  137.       """
  138.       try to figure what the mirror is from current sources.list
  139.  
  140.       do this by looing for matching DEFAULT_COMPONENT, current dist
  141.       in sources.list and then doing a http HEAD/ftp size request
  142.       to see if the uri is available on this server
  143.       """
  144.       self._debug("mirror_from_sources_list: %s" % self.current_dist_name)
  145.       sources = SourcesList(withMatcher=False)
  146.       seen = set()
  147.       for e in sources.list:
  148.         if e.disabled or e.invalid or not e.type == "deb":
  149.           continue
  150.         # check if we probed this mirror already
  151.         if e.uri in seen:
  152.           continue
  153.         # we are using the main mirror already, so we are fine
  154.         if (e.uri.startswith(default_uri) and 
  155.             e.dist == self.current_dist_name and
  156.             self.DEFAULT_COMPONENT in e.comps):
  157.           return uri
  158.         elif (e.dist == self.current_dist_name and
  159.               "main" in e.comps):
  160.           mirror_uri = e.uri+uri[len(default_uri):]
  161.           if url_downloadable(mirror_uri, self._debug):
  162.             return mirror_uri
  163.           seen.add(e.uri)
  164.       self._debug("no mirror found")
  165.       return ""
  166.  
  167.     def _expandUri(self, uri):
  168.         """
  169.         expand the uri so that it uses a mirror if the url starts
  170.         with a well know string (like archive.ubuntu.com)
  171.         """
  172.         # try to guess the mirror from the sources.list
  173.         if uri.startswith(self.DEFAULT_MIRROR):
  174.           self._debug("trying to find suitable mirror")
  175.           new_uri = self.mirror_from_sources_list(uri, self.DEFAULT_MIRROR)
  176.           if new_uri:
  177.             return new_uri
  178.         # if that fails, use old method
  179.         uri_template = Template(uri)
  180.         m = country_mirror()
  181.         new_uri = uri_template.safe_substitute(countrymirror=m)
  182.         # be paranoid and check if the given uri is really downloadable
  183.         try:
  184.             if not url_downloadable(new_uri, self._debug):
  185.               raise Exception("failed to download %s" % new_uri)
  186.         except Exception,e:
  187.             self._debug("url '%s' could not be downloaded" % e)
  188.             # else fallback to main server
  189.             new_uri = uri_template.safe_substitute(countrymirror='')
  190.         return new_uri
  191.  
  192.     def fetchDistUpgrader(self):
  193.         " download the tarball with the upgrade script "
  194.         self.tmpdir = tmpdir = tempfile.mkdtemp()
  195.         os.chdir(tmpdir)
  196.         # turn debugging on here (if required)
  197.         #apt_pkg.Config.Set("Debug::Acquire::http","1")
  198.         fetcher = apt_pkg.GetAcquire(self._progress)
  199.         if self.new_dist.upgradeToolSig != None:
  200.             uri = self._expandUri(self.new_dist.upgradeToolSig)
  201.             af = apt_pkg.GetPkgAcqFile(fetcher,uri, descr=_("Upgrade tool signature"))
  202.         if self.new_dist.upgradeTool != None:
  203.             self.uri = self._expandUri(self.new_dist.upgradeTool)
  204.             af = apt_pkg.GetPkgAcqFile(fetcher,self.uri, descr=_("Upgrade tool"))
  205.             if fetcher.Run() != fetcher.ResultContinue:
  206.                 return False
  207.             # check that both files are really there and non-null
  208.             for f in [os.path.basename(self.new_dist.upgradeToolSig),
  209.                       os.path.basename(self.new_dist.upgradeTool)]:
  210.                 if not (os.path.exists(f) and os.path.getsize(f) > 0):
  211.                     return False
  212.             return True
  213.         return False
  214.  
  215.     def runDistUpgrader(self):
  216.         #print "runing: %s" % script
  217.         args = [self.script]+self.run_options
  218.         if os.getuid() != 0:
  219.             os.execv("/usr/bin/sudo",["sudo"]+args)
  220.         else:
  221.             os.execv(self.script,args)
  222.  
  223.     def cleanup(self):
  224.       # cleanup
  225.       os.chdir("..")
  226.       # del tmpdir
  227.       shutil.rmtree(self.tmpdir)
  228.  
  229.     def run(self):
  230.         # see if we have release notes
  231.         if not self.showReleaseNotes():
  232.             return
  233.         if not self.fetchDistUpgrader():
  234.             self.error(_("Failed to fetch"),
  235.                   _("Fetching the upgrade failed. There may be a network "
  236.                     "problem. "))
  237.             return
  238.         if not self.extractDistUpgrader():
  239.             self.error(_("Failed to extract"),
  240.                   _("Extracting the upgrade failed. There may be a problem "
  241.                   "with the network or with the server. "))
  242.                   
  243.             return
  244.         if not self.verifyDistUprader():
  245.             self.error(_("Verfication failed"),
  246.                   _("Verifying the upgrade failed.  There may be a problem "
  247.                     "with the network or with the server. "))
  248.             self.cleanup()
  249.             return
  250.         if not self.authenticate():
  251.             self.error(_("Authentication failed"),
  252.                   _("Authenticating the upgrade failed. There may be a problem "
  253.                     "with the network or with the server. "))
  254.             self.cleanup()
  255.             return
  256.         try:
  257.           self.runDistUpgrader()
  258.         except OSError, e:
  259.           if e.errno == 13:
  260.             self.error(_("Can not run the upgrade"),
  261.                        _("This usually is caused by a system were /tmp "
  262.                          "is mounted noexec. Please remount without "
  263.                          "noexec and run the upgrade again."))
  264.             return False
  265.           else:
  266.             self.error(_("Can not run the upgrade"),
  267.                        _("The error message is '%s'." % e.strerror))
  268.         return True
  269.  
  270. if __name__ == "__main__":
  271.     d = DistUpgradeFetcherCore(None,None)
  272. #    print d.authenticate('/tmp/Release','/tmp/Release.gpg')
  273.     print "got mirror: '%s'" % d.mirror_from_sources_list("http://archive.ubuntu.com/ubuntu/dists/intrepid-proposed/main/dist-upgrader-all/0.93.34/intrepid.tar.gz", "http://archive.ubuntu.com/ubuntu")
  274.